package org.example.core.template;

import org.example.core.util.*;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 代码模板
 */
public class CodeTemplate {

    // TODO 这里可以额外支持在catch中遇到什么异常才会回滚或不回滚
    private static final PlaceholderTemplate transactionControlCodeTemplate = PlaceholderTemplate.parse("""
            %type:TransactionUtils%.begin(%filed:TransactionManager%,%field:Annotation%);
            try {
                %def:result% super.%name:methodName%(%args%);
                %type:TransactionUtils%.commit(%filed:TransactionManager%);
                %statement:return%
            } catch (Throwable e) {
                %type:TransactionUtils%.rollback(%filed:TransactionManager%);
                throw e;
            }
            """, '%', '%');

    /**
     * 生成事务控制代码
     *
     * @param annotationFieldName transactional注解实例所在的字段名称
     * @param transactionManagerFieldName PlatformTransactionManager实例所在的字段名称
     * @param returnType 方法的返回值类型全限定名, 如过无返回传入null
     * @param methodName 业务方法名称, 调用父类中的方法
     * @param parameterCount 业务方法参数数量
     * @return code
     */
    public static String generateTransactionControlCode(
            String annotationFieldName,
            String transactionManagerFieldName,
            String returnType,
            String methodName,
            int parameterCount
    ) {

        Map<String, Object> params = hashMapOf(
                "type:TransactionUtils", TransactionUtils.class.getCanonicalName(),
                "filed:TransactionManager", transactionManagerFieldName,
                "field:Annotation", annotationFieldName,
                "name:methodName", methodName
        );
        if (StringUtils.hasLength(returnType)) {
            params.put("def:result", returnType + " result=");
            params.put("statement:return", "return result;");
        }

        // javassist生成的方法参数格式为: $1、$2、$3、$4... $0代表this
        if (parameterCount > 1) {
            StringBuilder args = new StringBuilder();
            for (int i = 1; i <= parameterCount; i++) {
                args.append('$').append(i);
                if (i != parameterCount) {
                    args.append(',');
                }
            }
            params.put("args", args);
        }

        return "{" + transactionControlCodeTemplate.generate(params) + "}";
    }


    private static final PlaceholderTemplate injectionSetterCodeTemplate = PlaceholderTemplate.parse("""
            java.lang.reflect.Field field = {type:ReflectUtils}.getDeclaredField(this.getClass().getSuperclass(), "{fieldName}");
            field.setAccessible(true);
            field.set(this, $1);
            """, '{', '}');

    public static String generateInjectionSetterCode(String fieldName) {
        return "{" + injectionSetterCodeTemplate.generate(Map.of(
                "type:ReflectUtils", ReflectUtils.class.getCanonicalName(),
                "fieldName", fieldName
        )) + "}";
    }

    private static final PlaceholderTemplate constructorCodeTemplate = PlaceholderTemplate.parse("""
            super({args});
            {type:AnnotationStoreUtils}.fill(this);
            """, '{', '}');

    public static String generateConstructorCode(int parameterCount) {
        StringBuilder argsBuilder = new StringBuilder();
        for (int i = 1; i <= parameterCount; i++) {
            argsBuilder.append('$').append(i);
            if (i != parameterCount) {
                argsBuilder.append(',');
            }
        }
        return "{" + constructorCodeTemplate.generate(Map.of(
                "args", argsBuilder.toString(),
                "type:AnnotationStoreUtils", AnnotationStoreUtils.class.getCanonicalName()
        )) + "}";
    }

    private static Map<String, Object> hashMapOf(Object...args) {
        HashMap<String, Object> hashMap = new HashMap<>(args.length / 2);
        for (int i = 0; i < args.length; i += 2) {
            hashMap.put(args[i].toString(), args[i + 1]);
        }
        return hashMap;
    }

}
